CloudFormationでスタックをネストし、EC2を起動させてみた
CloudFormationでのスタックのネスト
CloudFormationでは、スタックをネストさせること可能です。
ネストさせる際には、テンプレート(ファイル)を分割して記述することができるので、一度作成したテンプレートを使い回すことができます。
本記事では、実際にスタックをネストし、EC2の起動を行なっていきます。
作成する構成
EC2をPublic Subnet上に立てます。今回は使用しませんが、Private Subnetも構築します。
各リソースのテンプレート作成
ファイル構成は以下のように行います。
├── ec2 │ ├── ec2.yaml │ └── sg.yaml ├── main.yaml ├── subnet │ ├── private │ │ └── subnet.yaml │ └── public │ └── subnet.yaml └── vpc └── vpc.yaml
EC2を作成する際に、セキュリティグループのテンプレートを呼び出すようにします。
親テンプレートの作成
各テンプレートを呼び出す親テンプレートを作成します。
AWSTemplateFormatVersion: "2010-09-09" Description: nesting stack Resources: VPC: Type: AWS::CloudFormation::Stack Properties: TemplateURL: vpc/vpc.yaml Parameters: TagName : "VPCTEST" PrivateSubnet: Type: AWS::CloudFormation::Stack Properties: TemplateURL: subnet/private/subnet.yaml Parameters: VPC: !GetAtt VPC.Outputs.VPCID PublicSubnet: Type: AWS::CloudFormation::Stack Properties: TemplateURL: subnet/public/subnet.yaml Parameters: VPC: !GetAtt VPC.Outputs.VPCID EC2Instance: Type: AWS::CloudFormation::Stack Properties: TemplateURL: ec2/ec2.yaml Parameters: VPC: !GetAtt VPC.Outputs.VPCID KeyName: hogehoge Subnet: !GetAtt PublicSubnet.Outputs.SubnetID
VPCテンプレートの作成
VPCのIDを渡すのでOutputsに指定しています。
AWSTemplateFormatVersion: '2010-09-09' Description: 'VPC for nesting' Parameters: VPCCIDR: Type: String Default: "10.1.0.0/16" TagName : Type: String Default: "nesting" Resources: VPC: Type: AWS::EC2::VPC Properties: CidrBlock: !Ref VPCCIDR Tags: - Key: Name Value: !Ref TagName Outputs: VPCID: Value: !Ref VPC
サブネットテンプレートの作成
パブリック、プライベートサブネットそれぞれの作成を行います。
AWSTemplateFormatVersion: '2010-09-09' Description: 'private subnet for nesting' Parameters: AZ: Type: AWS::EC2::AvailabilityZone::Name Default: ap-northeast-1a TagName: Type: String Default: "subnetnesting" VPC: Type: AWS::EC2::VPC::Id Resources: PrivateSubnet: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Ref AZ VpcId: !Ref VPC CidrBlock: 10.1.1.0/24 Tags: - Key: Name Value: !Ref TagName PrivateRouteTable: Type: "AWS::EC2::RouteTable" Properties: VpcId: !Ref VPC Tags: - Key: Name Value: !Ref TagName PrivateSubnetTableAssociation: Type: "AWS::EC2::SubnetRouteTableAssociation" Properties: SubnetId: !Ref PrivateSubnet RouteTableId: !Ref PrivateRouteTable Outputs: SubnetID: Value: !Ref PrivateSubnet
AWSTemplateFormatVersion: '2010-09-09' Description: 'public subnet for nesting' Parameters: AZ: Type: AWS::EC2::AvailabilityZone::Name Default: ap-northeast-1a TagName: Type: String Default: "subnetnesting" VPC: Type: AWS::EC2::VPC::Id Resources: PublicSubnet: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Ref AZ VpcId: !Ref VPC CidrBlock: 10.1.2.0/24 Tags: - Key: Name Value: !Ref TagName PublicRouteTable: Type: "AWS::EC2::RouteTable" Properties: VpcId: !Ref VPC Tags: - Key: Name Value: !Ref TagName InternetGateway: Type: "AWS::EC2::InternetGateway" Properties: Tags: - Key: Name Value: !Ref TagName InternetGatewayAttachment: Type: "AWS::EC2::VPCGatewayAttachment" Properties: InternetGatewayId: !Ref InternetGateway VpcId: !Ref VPC PublicRoute: Type: "AWS::EC2::Route" Properties: RouteTableId: !Ref PublicRouteTable DestinationCidrBlock: "0.0.0.0/0" GatewayId: !Ref InternetGateway PublicSubnetTableAssociation: Type: "AWS::EC2::SubnetRouteTableAssociation" Properties: SubnetId: !Ref PublicSubnet RouteTableId: !Ref PublicRouteTable Outputs: SubnetID: Value: !Ref PublicSubnet
EC2テンプレートの作成
EC2では、セキュリティグループのテンプレートを呼び出すように記述します。
動作確認のため、LAMPを構築するようにユーザデータを指定します。
AWSTemplateFormatVersion: '2010-09-09' Description: 'VPC for nesting' Parameters: ImageId: Type: String Default: "ami-0701e21c502689c31" KeyName: Type: String Subnet: Type: AWS::EC2::Subnet::Id VPC: Type: AWS::EC2::VPC::Id TagName : Type: String Default: "nesting" Resources: EC2SG: Type: AWS::CloudFormation::Stack Properties: TemplateURL: sg.yaml Parameters: VPC: !Ref VPC EC2Instance: Type: AWS::EC2::Instance Properties: ImageId: !Ref ImageId KeyName: !Ref KeyName InstanceType: t2.micro NetworkInterfaces: - AssociatePublicIpAddress: "true" DeviceIndex: "0" SubnetId: !Ref Subnet GroupSet: - !GetAtt EC2SG.Outputs.SGID UserData: !Base64 | #!/bin/bash yum update -y amazon-linux-extras install -y lamp-mariadb10.2-php7.2 php7.2 yum install -y httpd mariadb-server systemctl start httpd systemctl enable httpd usermod -a -G apache ec2-user chown -R ec2-user:apache /var/www chmod 2775 /var/www && find /var/www -type d -exec sudo chmod 2775 {} \; find /var/www -type f -exec sudo chmod 0664 {} \; echo "<?php phpinfo(); ?>" > /var/www/html/phpinfo.php systemctl start mariadb mysqladmin password hogehoge1111 yum install php-mbstring php-xml -y systemctl restart httpd systemctl restart php-fpm wget https://www.phpmyadmin.net/downloads/phpMyAdmin-latest-all-languages.tar.gz mkdir /var/www/html/phpMyAdmin && tar -xvzf phpMyAdmin-latest-all-languages.tar.gz -C /var/www/html/phpMyAdmin --strip-components 1 rm phpMyAdmin-latest-all-languages.tar.gz systemctl start mariadb Tags: - Key: Name Value: !Ref TagName
セキュリティグループテンプレートの作成
セキュリティグループのIDをEC2に渡す必要があるため、Outputsで指定します。
AWSTemplateFormatVersion: '2010-09-09' Description: 'SG for nesting' Parameters: MyIP: Type: String Default: "0.0.0.0/0" VPC: Type: AWS::EC2::VPC::Id TagName : Type: String Default: "nesting" Resources: EC2SG: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: "test demo" VpcId: !Ref VPC SecurityGroupIngress: - IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIp: !Ref MyIP - IpProtocol: tcp FromPort: 22 ToPort: 22 CidrIp: !Ref MyIP Tags: - Key: Name Value: !Ref TagName Outputs: SGID: Value: !Ref EC2SG
スタック作成の準備をする
このままの記述では、ローカルファイルを参照できないので、S3 バケットへアップロードします。
$ aws s3 mb s3://nested-stack-artifact
バケット作成後、下記コマンドでアップロードします。
aws cloudformation package --template-file main.yaml \ --s3-bucket nested-stack-artifact \ --output-template-file artifact.yml
正常に実行できると、artifact.yml
が作成されています。
中身を見てみると、先ほどまでローカルファイルを指定していたものがs3のファイル参照に変更されていることが確認できます。
AWSTemplateFormatVersion: '2010-09-09' Description: nesting stack Resources: VPC: Type: AWS::CloudFormation::Stack Properties: TemplateURL: https://s3.ap-northeast-1.amazonaws.com/nested-stack-artifact/hogehoge.template Parameters: TagName: VPCTEST PrivateSubnet: Type: AWS::CloudFormation::Stack Properties: TemplateURL: https://s3.ap-northeast-1.amazonaws.com/nested-stack-artifact/hogehoge.template Parameters: VPC: Fn::GetAtt: - VPC - Outputs.VPCID PublicSubnet: Type: AWS::CloudFormation::Stack Properties: TemplateURL: https://s3.ap-northeast-1.amazonaws.com/nested-stack-artifact/hogehoge.template Parameters: VPC: Fn::GetAtt: - VPC - Outputs.VPCID EC2Instance: Type: AWS::CloudFormation::Stack Properties: TemplateURL: https://s3.ap-northeast-1.amazonaws.com/nested-stack-artifact/hogehoge.template Parameters: VPC: Fn::GetAtt: - VPC - Outputs.VPCID KeyName: hogehoge Subnet: Fn::GetAtt: - PublicSubnet - Outputs.SubnetID
スタックの作成
では、最後にスタックの作成を行います。先程のartifact.yml
を指定します。
aws cloudformation deploy --template-file artifact.yml --stack-name nested-stack-demo
コンソール上を確認すると、作成したスタックとネストされたスタックが確認できます。
しばらく待つと、CREATE_COMPLETEとなります。
まとめ
本記事では、ネスタされたスタックを作成してみましたが、再利用できるのは非常に魅力的だと感じました。
一方で、どこまで分割していくかが悩ましいところです。細かく分割してしまうと、逆にファイルの管理が大変になりそうですので、機能ごとにスタックをまとめて、再利用していくのが良さそうです。